// * 原生的原型

let obj = {}

// obj = {} 和 obj = new Object() 是一个意思。
// 其中 Object 就是一个内建的对象构造函数，它的 prototype 属性指向了一个带有 toString 和其他方法的巨大对象。
// 构造函数的 prototype 属性，会成为实例对象的原型。

console.log(String(obj)) // [object Object]

// *****************************************************************************************

// * 其他内建对象：Array、Date、Function 及其他，都在 prototype 属性上挂在了方法。

let arr = [1, 2, 3]

// 当我们创建一个数组 [1, 2, 3]，在内部会默认使用 new Array() 构造器。
// arr = [] 和 arr = new Array() 是一个意思。arr 的原型就是 Array.prototype。

// * 所有内建的原型都指向 Object.prototype，它的 prototype 属性指向了 null。

console.log(arr.__proto__ === Array.prototype) // true
console.log(arr.__proto__.__proto__ === Object.prototype) // true
console.log(arr.__proto__.__proto__.__proto__ === null) // true

// *****************************************************************************************

// * null 和 undefined 没有对象包装器，所以它们没有方法和属性，所以也就没有相应的原型对象。

// *****************************************************************************************

// * 更改原生对象的原型：原生的原型是可以被修改的。【不推荐这么做】

// 例子1：我们向 Function.prototype 中添加一个属性，这个属性将对所有的函数都是可用的。
Function.prototype.like = '一个人'
function fn() {}
console.log(fn.like) // 一个人

// 例子2：我们向 String.prototype 中添加一个属性，这个属性将对所有的字符串都是可用的。
String.prototype.like = '两个人'
console.log('str'.like) // 两个人

// * 在现代编程中，只有一种情况下允许修改原生原型。那就是 polyfilling。
// * Polyfilling 是一个术语，表示某个方法在 JS 规范中已存在，但是特定的 JS 引擎尚不支持该方法，那么我们可以通过手动实现它。
// 例子：
if (!String.prototype.trim) {
	// ...
}

// *****************************************************************************************

// * 从原型中借用

let objs = {
	0: 'a',
	1: 'b',
	length: 2,
}

objs.join = Array.prototype.join

console.log(objs.join(',')) // a,b
